package com.edol.painting.utils;

import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.os.StatFs;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;

import com.doodle.common.utils.SDCardHelper;
import com.doodle.common.utils.Utils;
import com.edol.painting.R;
import com.edol.painting.data.PaintingDatabase;

import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.List;

public class PainterUtils {

	private static String sSaveFilePath = null;
	private static String sTempFilePath = null;
	private static String sExportFilePath = null;

	public static void initSaveFilePath(Context context) {
		// Begin : modify by liuqiang 2014-5-8 for bug#45844
		if (sSaveFilePath == null || !new File(sSaveFilePath).exists()) {
			File filesDir = context.getExternalFilesDir(null);
			if (filesDir != null) {
				File paintingsDir = new File(filesDir, Constant.PAINTINGS_DIR);
				if (!paintingsDir.exists() || !paintingsDir.isDirectory()) {
					paintingsDir.mkdirs();
				}
				sSaveFilePath = paintingsDir.toString();
			}
		}
		// End : modify by liuqiang 2014-5-8 for bug#45844
	}
	
	public static String getSaveFilePath(Context context) {
		initSaveFilePath(context);
		return sSaveFilePath;
	}
	
	public static void initTempFilePath(Context context) {
		if (sTempFilePath == null) {
			File filesDir = context.getExternalFilesDir(null);
			if (filesDir != null) {
				File tempsDir = new File(filesDir, Constant.PAINTER_TEMP_DIR);
				if (!tempsDir.isDirectory()) {
					tempsDir.mkdirs();
				}
				sTempFilePath = tempsDir.toString();
			}
		}
	}
	
	public static void initExportFilePath() {
		if (sExportFilePath == null) {
			File filesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
			if (filesDir != null) {
				File tempsDir = new File(filesDir, Constant.PAINTER_EXPORT_DIR);
				if (!tempsDir.isDirectory()) {
					tempsDir.mkdirs();
				}
				sExportFilePath = tempsDir.toString();
			}
		}
	}

	private static final SimpleDateFormat sFormater = new SimpleDateFormat("yyyyMMdd-HHmmss");
	private static final String sIndexFormat = "%s-%03d";

	public static File createNewPaintingFile(Context context, long currentTime, boolean forExport) {
		if (forExport) {
			initExportFilePath();
		} else {
			initSaveFilePath(context);
		}

		String created = sFormater.format(currentTime);
		String result = "D" + created.substring(3);

		int index = 0;
		File file = null;
		do {
			String name = index == 0 ? result : String.format(sIndexFormat, result, index);
			file = new File(forExport ? sExportFilePath : sSaveFilePath, name + Constant.PAINTING_FILE_EXTENSION);
			if (!file.exists()) {
				break;
			}
		} while (++index < 50);
		
		if (index == 50) {
			return null;
		}
		return file;
	}
	
	public static String getTempFilePath(Context context, int id) {
		initTempFilePath(context);
		return sTempFilePath + "/" + id + ".tmp";
	}
	
	public static void clearTempFiles(Context context) {
		initTempFilePath(context);
		if (sTempFilePath != null) {
			File tempDir = new File(sTempFilePath);
			File[] files = tempDir.listFiles(new FilenameFilter() {
				@Override
				public boolean accept(File dir, String filename) {
					return filename != null && filename.endsWith(".tmp");
				}
			});
			if (files != null) {
				for (File file : files) {
					file.delete();
				}
			}
		}
	}

	public static float getDistance(float x1, float y1, float x2, float y2) {
		float xoff = x1 - x2;
		float yoff = y1 - y2;
		return (float) Math.sqrt(xoff * xoff + yoff * yoff);
	}

	public static float getAngle(float xVelocity, float yVelocity) {
		float angle = 90;
		if (Math.abs(xVelocity) > 1e-5) {
			float slope = yVelocity / xVelocity;
			angle = (float) (Math.atan(slope) * 180 / Math.PI);
			if (xVelocity < 0 && yVelocity < 0) {
				angle += 180;
			} else if (xVelocity > 0 && yVelocity < 0) {
				angle += 360;
			} else if (xVelocity < 0 && yVelocity > 0) {
				angle += 180;
			}
		} else {
			if (yVelocity < 0) {
				angle = 270;
			}
		}
		return angle;
	}
	
	public static final long UNAVAILABLE = -1L;
	public static final long PREPARING = -2L;
	public static final long UNKNOWN_SIZE = -3L;
	public static final long LOW_STORAGE_THRESHOLD = 1024 * 1024 * 50;
	
	public static long getAvailableSpace() {
		try {
			StatFs stat = new StatFs(Utils.getExternalStorageDirectory().getAbsolutePath());
			return stat.getAvailableBlocks() * (long) stat.getBlockSize();
		} catch (Exception e) {
			Log.i("@@@", "Fail to access external storage", e);
		}
		return UNKNOWN_SIZE;
	}

	public static long checkStorage(Context context) {
		if (!SDCardHelper.getInstance().isMounted()) {
			return UNAVAILABLE;
		}
		String state = Environment.getExternalStorageState();
		if (Environment.MEDIA_CHECKING.equals(state)) {
			return PREPARING;
		} else if (!Environment.MEDIA_MOUNTED.equals(state)) {
			return UNAVAILABLE;
		} else {
			return getAvailableSpace();
		}
	}

	public static String getStorageHint(Context context, long storageSpace) {
		if (storageSpace == UNAVAILABLE) {
			return context.getString(R.string.no_storage);
		} else if (storageSpace == PREPARING) {
			return context.getString(R.string.preparing_sd);
		} else if (storageSpace == UNKNOWN_SIZE) {
			return context.getString(R.string.access_sd_fail);
		} else if (storageSpace < LOW_STORAGE_THRESHOLD) {
			return context.getString(R.string.space_is_low_content);
		}
		return null;
	}
	
	public static boolean checkStorageOrShowToast(Context context) {
		long state = checkStorage(context);
		String massage = getStorageHint(context, state);
		if (massage != null) {
			Toast.makeText(context, massage, Toast.LENGTH_SHORT).show();
			return false;
		} else {
			return true;
		}
	}
	
	private static final int[] gRandomTable = { 
			-87, 58, -98, 56, 49, -86, -88,
			76, 49, 27, 33, -20, -32, 93, -73, -34, -93, 12, -83, 90, 51, 45,
			-43, 71, -47, -83, -22, 47, 84, 77, 18, 48, 55, 91, -84, 90, -40,
			-47, 41, -18, 25, -59, -2, 37, -44, -57, -26, 3, -55, 21, -68, 73,
			95, -86, 11, -33, 0, -69, 2, 32, 36, -35, 92, 71, -91, 84, 24, -24,
			76, -63, -95, 54, 70, 34, -35, 10, -53, 20, 15, -90, 42, 74, 1,
			-38, 44, 44, 52, 41, -20, 49, -11, -71, 50, -91, 46, -40, -52, 26, 27, -28, };
	
	public static int getRandNum(int num) {
		return gRandomTable[num % gRandomTable.length];
	}

	private static final int[] gAlphaCurveTable = { 
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1,
			1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 
			3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 
			8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 
			14, 14, 15, 15, 16, 16, 16, 17, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 
			23, 23, 24, 25, 25, 26, 26, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 
			38, 38, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 50, 52, 53, 54, 55, 57, 58, 60, 
			61, 62, 64, 65, 67, 68, 70, 72, 73, 75, 77, 79, 80, 82, 84, 86, 88, 90, 92, 94, 
			96, 98, 100, 102, 105, 107, 109, 111, 114, 116, 118, 121, 123, 125, 128, 
			130, 133, 135, 138, 140, 143, 145, 148, 150, 153, 156, 158, 161, 164, 166, 
			169, 172, 174, 177, 180, 183, 185, 188, 191, 194, 197, 200, 202, 205, 208, 
			211, 214, 217, 220, 222, 225, 228, 231, 234, 237, 240, 243, 246, 249, 252, 
			255,
	};

	public static int getAlphaCurveValue(int id) {
		return gAlphaCurveTable[id % gAlphaCurveTable.length];
	}
	
	public static int getAlphaCurveIndex(int num) {
		for (int i = 0; i < gAlphaCurveTable.length; i++) {
			if (num <= gAlphaCurveTable[i]) {
				return i;
			}
		}
		return 0;
	}
	
	private static int sHasNfc = -1;

	public static boolean hasNFC() {
		if (sHasNfc == -1) {
			Method hasNFCMethod;
			try {
				Class c = Class.forName("android.os.BuildExt");
				hasNFCMethod = c.getMethod("hasNFC");
				Object obj = hasNFCMethod.invoke(null);
				sHasNfc = (Boolean) obj ? 1 : 0;
			} catch (Exception e) {
			}
		}
		return sHasNfc == 1;
	}

	public static boolean isNfcConnect(Context context) {
		boolean isConnect = false;
		try {
            Class<?> c = Class.forName("android.nfc.NfcAdapterMzExt");
			Method getNfcAdapterMethod = c.getMethod("getNfcAdapterMzExt", Context.class);
			Object obj = getNfcAdapterMethod.invoke(null, context);
			Method isMzNfcP2pConnectedMethod = c.getMethod("isMzNfcP2pConnected");
			Object connect = isMzNfcP2pConnectedMethod.invoke(obj);
			isConnect = (Boolean) connect;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return isConnect;
	}

	public static String getFilePathFromDB(Context context, long id) {
        Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        String filePath = null;
        Cursor cursor = null;
        try {
            cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, BaseColumns._ID + "=" + id, null, null);
            if (cursor != null && cursor.moveToFirst()) {
                filePath = cursor.getString(0);
                Log.i("liu", "filePath="+filePath);
            }
        } finally {
            if (cursor != null) {
                cursor.close();
                cursor = null;
            }
        }
        return filePath;
    }

    /*public static Bitmap loadBitmapFromFile(String path, int width, int height) {
        long time = System.currentTimeMillis();
        if (TextUtils.isEmpty(path)) {
            return null;
        }

        BitmapFactory.Options op = new BitmapFactory.Options();
        op.inSampleSize = 1;
        op.inMutable = true;
        Bitmap bitmap = null;
        int num = 0;
        while (num <= 4) {
            try {
                bitmap = BitmapFactory.decodeFile(path, op);
                if (bitmap != null) {
                    break;
                }
            } catch(OutOfMemoryError e) {
                op.inSampleSize *= 2;
                bitmap = null;
            }
            num++;
        }

        Log.i("Utils", "w = " + bitmap.getWidth()+",h="+bitmap.getHeight()+",inSampleSize="+op.inSampleSize);
        if (bitmap != null) {
            int w = bitmap.getWidth();
            int h = bitmap.getHeight();
            if (w == width && h == height) {
                Log.i("Utils", "loadBitmapFromFile 0 pay time:" + (System.currentTimeMillis() - time));
                return bitmap;
            }
        }

        Bitmap dst = BitmapUtils.createBitmap(width, height, MeasuredAsyncDrawable.DRAWABLE_TYPE_IMAGE, Config.ARGB_8888);
        Canvas dstCanvas = new Canvas(dst);
        dstCanvas.drawColor(0x0, Mode.CLEAR);
        dstCanvas.drawBitmap(bitmap, null, new Rect(0, 0, width, height), null);

        bitmap.recycle();
        bitmap = null;
        Log.i("Utils", "loadBitmapFromFile 1 pay time:" + (System.currentTimeMillis() - time));
        return dst;
    }*/
    
    public static int getOrientation(InputStream is) {
        if (is == null) {
            return 0;
        }

        byte[] buf = new byte[8];
        int length = 0;

        // ISO/IEC 10918-1:1993(E)
        while (read(is, buf, 2) && (buf[0] & 0xFF) == 0xFF) {
            int marker = buf[1] & 0xFF;

            // Check if the marker is a padding.
            if (marker == 0xFF) {
                continue;
            }

            // Check if the marker is SOI or TEM.
            if (marker == 0xD8 || marker == 0x01) {
                continue;
            }
            // Check if the marker is EOI or SOS.
            if (marker == 0xD9 || marker == 0xDA) {
                return 0;
            }

            // Get the length and check if it is reasonable.
            if (!read(is, buf, 2)) {
                return 0;
            }
            length = pack(buf, 0, 2, false);
            if (length < 2) {
                return 0;
            }
            length -= 2;

            // Break if the marker is EXIF in APP1.
            if (marker == 0xE1 && length >= 6) {
                if (!read(is, buf, 6)) return 0;
                length -= 6;
                if (pack(buf, 0, 4, false) == 0x45786966 &&
                    pack(buf, 4, 2, false) == 0) {
                    break;
                }
            }

            // Skip other markers.
            try {
                is.skip(length);
            } catch (IOException ex) {
                return 0;
            }
            length = 0;
        }

        // JEITA CP-3451 Exif Version 2.2
        if (length > 8) {
            int offset = 0;
            byte[] jpeg = new byte[length];
            if (!read(is, jpeg, length)) {
                return 0;
            }

            // Identify the byte order.
            int tag = pack(jpeg, offset, 4, false);
            if (tag != 0x49492A00 && tag != 0x4D4D002A) {
                return 0;
            }
            boolean littleEndian = (tag == 0x49492A00);

            // Get the offset and check if it is reasonable.
            int count = pack(jpeg, offset + 4, 4, littleEndian) + 2;
            if (count < 10 || count > length) {
                return 0;
            }
            offset += count;
            length -= count;

            // Get the count and go through all the elements.
            count = pack(jpeg, offset - 2, 2, littleEndian);
            while (count-- > 0 && length >= 12) {
                // Get the tag and check if it is orientation.
                tag = pack(jpeg, offset, 2, littleEndian);
                if (tag == 0x0112) {
                    // We do not really care about type and count, do we?
                    int orientation = pack(jpeg, offset + 8, 2, littleEndian);
                    switch (orientation) {
                        case 1:
                            return 0;
                        case 3:
                            return 180;
                        case 6:
                            return 90;
                        case 8:
                            return 270;
                    }
                    return 0;
                }
                offset += 12;
                length -= 12;
            }
        }
        return 0;
    }
    
    private static int pack(byte[] bytes, int offset, int length,
            boolean littleEndian) {
        int step = 1;
        if (littleEndian) {
            offset += length - 1;
            step = -1;
        }

        int value = 0;
        while (length-- > 0) {
            value = (value << 8) | (bytes[offset] & 0xFF);
            offset += step;
        }
        return value;
    }
    
    private static boolean read(InputStream is, byte[] buf, int length) {
        try {
            return is.read(buf, 0, length) == length;
        } catch (IOException ex) {
            return false;
        }
    }

    public static void setAlertDialogPositiveButtonTextColor(AlertDialog dialog){
        try {
            Method b = AlertDialog.class.getMethod("setButtonTextColor", new Class[] { Integer.TYPE, Integer.TYPE });
            b.invoke(dialog, new Object[] { AlertDialog.BUTTON_POSITIVE, R.color.painter_theme_color });
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    
    public static void setProgressDialog(ProgressDialog progressDialog, String str) {
    	progressDialog.setMessage(str);
    	progressDialog.setCancelable(false);
    	progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
		try {
            Method method = ProgressDialog.class.getDeclaredMethod("setSpinnerBackground", new Class[] { android.graphics.drawable.Drawable.class });
            method.setAccessible(true);
            method.invoke(progressDialog, new Object[] {null});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    
    public static int convertDimen(int dimen) {
		return Math.round(dimen * Constant.PAINTING_WIDTH / 1152);
	}

    public static boolean checkFile(String path) {
        File file = new File(path);
        return file == null ? true : file.exists() ? true : false;
    }
    
    public static boolean checkPaintingEntry(PaintingDatabase.PaintingEntry pe) {
    	String str = pe.mDirectory.substring(0, pe.mDirectory.length() - 4);
		if (checkFile(str + "-2.png") && checkFile(pe.mDirectory) && checkFile(pe.mThumbnail)) {
			return false;
		}
		return true;
    }

    public static boolean checkNoLayerPaintingEntry(PaintingDatabase.PaintingEntry pe) {
        if (checkFile(pe.mDirectory) && checkFile(pe.mThumbnail)) {
            return false;
        }
        return true;
    }
    
    public static boolean checkPaintingEntrys(List<PaintingDatabase.PaintingEntry> lists) {
        boolean b = false;
		for (int i = 0; i < lists.size(); i++) {
			PaintingDatabase.PaintingEntry pe = lists.get(i);
			if (pe.mLayer ? checkPaintingEntry(pe) : checkNoLayerPaintingEntry(pe)) {
                PaintingDatabase.getInstance().deleteByIds(new long[] { pe.mId }, null);
                b = true;
			}
		}
		return b;
	}

    public static void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void clearInput(Context context) {
        InputMethodManager imm = (InputMethodManager)context.getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        try {
            Method method = InputMethodManager.class.getDeclaredMethod("finishInputLocked");
            method.setAccessible(true);
            method.invoke(imm);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
